package com.easylinkin.linkappapi.lobar.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.easylinkin.linkappapi.common.service.AppSendMessageService;
import com.easylinkin.linkappapi.common.service.CommonService;
import com.easylinkin.linkappapi.common.utils.excel.ExcelConstant;
import com.easylinkin.linkappapi.common.utils.excel.ExcelTools;
import com.easylinkin.linkappapi.common.utils.io.OutputStreamUtil;
import com.easylinkin.linkappapi.lobar.constant.LobarConstant;
import com.easylinkin.linkappapi.lobar.constant.LobarConstant.WaringType;
import com.easylinkin.linkappapi.lobar.constant.LobarConstant.WarningMsgTemplate;
import com.easylinkin.linkappapi.lobar.dto.UserClockDTO;
import com.easylinkin.linkappapi.lobar.entity.UserOutinWarning;
import com.easylinkin.linkappapi.lobar.entity.WarningConfig;
import com.easylinkin.linkappapi.lobar.entity.emp.EmpUserBaseDTO;
import com.easylinkin.linkappapi.lobar.entity.vo.UserOutinWarningVo;
import com.easylinkin.linkappapi.lobar.mapper.UserClockMapper;
import com.easylinkin.linkappapi.lobar.mapper.UserOutinWarningMapper;
import com.easylinkin.linkappapi.lobar.service.UserOutinWarningService;
import com.easylinkin.linkappapi.lobar.service.UserProjectService;
import com.easylinkin.linkappapi.security.context.LinkappUserContextProducer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import site.morn.rest.RestBuilders;
import site.morn.rest.RestMessage;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * UserOutinWarning表服务实现类
 *
 * @author zhengwen
 * @date 2022/10/20
 */
@Slf4j
@Service("appUserOutinWarningService")
public class UserOutinWarningServiceImpl extends ServiceImpl
        <UserOutinWarningMapper, UserOutinWarning> implements UserOutinWarningService {
    @Resource
    private CommonService commonService;
    @Resource
    private LinkappUserContextProducer linkappUserContextProducer;
    @Resource
    private UserClockMapper userClockMapper;

    @Autowired
    private UserProjectService userProjectService;

    @Resource
    private UserOutinWarningMapper userOutinWarningMapper;

    @Autowired
    private AppSendMessageService appSendMessageService;


    @Override
    public boolean saveOne(UserOutinWarning appUserOutinWarning) {
        commonService.setCreateAndModifyInfo(appUserOutinWarning);
        // appUserOutinWarning.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        validParamRequired(appUserOutinWarning);
        validRepeat(appUserOutinWarning);
        validParamFormat(appUserOutinWarning);
        return save(appUserOutinWarning);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateOne(UserOutinWarning appUserOutinWarning) {
        Assert.notNull(appUserOutinWarning.getId(), "id不能为空");
        commonService.setModifyInfo(appUserOutinWarning);
        //appUserOutinWarning.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        validRepeat(appUserOutinWarning);
        validParamFormat(appUserOutinWarning);
        return updateById(appUserOutinWarning);
    }

    @Override
    public IPage<UserOutinWarning> selectPage(Page page, UserOutinWarning userOutinWarning) {
        //appUserOutinWarning.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        //return baseMapper.selectPage(page, userOutinWarning);
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByIds(List<Long> idList) {
        return removeByIds(idList);
    }

    @Override
    public void export(UserOutinWarning appUserOutinWarning, HttpServletRequest request, HttpServletResponse
            response) {

        IPage<UserOutinWarning> page = selectPage(new Page(0, -1), appUserOutinWarning);
        List<UserOutinWarning> records = page.getRecords();
        List
                <UserOutinWarningVo> appUserOutinWarningVos = new ArrayList<>();
        for (UserOutinWarning expert : records) {
            appUserOutinWarningVos.add(new UserOutinWarningVo(expert));
        }

        String keyValue = "名称:name,租户id:tenantId";
        String title = "UserOutinWarning导出数据";
        String fileName = title + ".xls";
        try {
            OutputStream outputStream = OutputStreamUtil
                    .getOutputStream(request, response, fileName);
            ExcelTools.exportExcel(outputStream, keyValue, appUserOutinWarningVos, ExcelConstant.XLS, title);
            response.flushBuffer();
            outputStream.close();
        } catch (IOException e) {
            log.error("excel导出失败", e);
            throw new RuntimeException("excel导出失败！IOException异常" + e.getMessage());
        } catch (Exception e) {
            log.error("excel导出失败", e);
            throw new RuntimeException("excel导出失败！" + e.getMessage());
        }
    }

    @Override
    public RestMessage exeUserOutinWarning(WarningConfig conf, Date now) {
        String tenantId = conf.getTenantId();
        //查询施工区各人最后一次进记录
        UserClockDTO clockDto = new UserClockDTO();
        clockDto.setTenantId(tenantId);
        clockDto.setStartTime(DateUtil.beginOfDay(now));
        clockDto.setEndTime(now);
        //服务区域：1施工区，2办公区，3生活区
        clockDto.setServiceArea(1);
        List<UserClockDTO> userClockList = userClockMapper.queryLastUserClockList(clockDto);
        //如果没有记录，那就不用往下了
        if (CollectionUtil.isNotEmpty(userClockList)) {
            //先去重
            List<UserClockDTO> finUserClockList = userClockList.stream().collect(Collectors.collectingAndThen(
                    Collectors.toCollection(() -> new TreeSet<>(
                            Comparator.comparing(p -> p.getDirection() + ";" + p.getUserId()))), ArrayList::new));

            //进出记录都转map备用
            Map<String, UserClockDTO> clockInMap = finUserClockList.stream().filter(uc -> uc.getDirection().equals(1)).collect(Collectors.toMap(UserClockDTO::getUserId, c -> c));
            //如果都是出记录，就不用管
            if (CollectionUtil.isNotEmpty(clockInMap)) {
                Map<String, UserClockDTO> clockOutMap = finUserClockList.stream().filter(uc -> uc.getDirection().equals(2)).collect(Collectors.toMap(UserClockDTO::getUserId, c -> c));
                clockInMap.entrySet().stream().forEach(en -> {
                    String userId = en.getKey();
                    UserClockDTO clockIn = en.getValue();
                    UserClockDTO clockOut = null;
                    if (CollectionUtil.isNotEmpty(clockOutMap)) {
                        clockOut = clockOutMap.get(userId);
                    }
                    UserOutinWarning outinWarn = convertOutinWarn(conf, now, clockIn, clockOut);
                    if (outinWarn != null) {
                        baseMapper.insert(outinWarn);
                        appSendMessageService.sendAppWarning(outinWarn.getId(), WaringType.USER_OUTIN_WARNING, outinWarn.getWarningMsg(), tenantId);
                    }
                });
            }
        }

        return RestBuilders.successBuilder().build();
    }

    /**
     * 转换人员进出场异常告警
     *
     * @param conf     配置
     * @param now      当前时间
     * @param clockIn  进场记录
     * @param clockOut 出场记录
     * @return 进出场异常告警记录
     */
    private UserOutinWarning convertOutinWarn(WarningConfig conf, Date now, UserClockDTO clockIn, UserClockDTO clockOut) {
        /**
         * 1、出检开、停留开
         *  a、有进有出  不报
         *  b、有进无出  最后一次进到检查时间点相差时长 >= 停留时长？判断。 true 报 false 不报
         * 2、出检开、停留关
         *  a、有进有出 不报
         *  b、有进无出 报
         * 3、出检关、停留开  无效
         *
         * 总结:
         * 1、有进有出不报
         * 2、有进无处判断开关->判断 进-检查点时长 >= 停留时长
         *
         */
        //有进有出不报
        if (clockIn != null && clockOut != null) {
            return null;
        }
        UserOutinWarning userOutinWarning = new UserOutinWarning();
        userOutinWarning.setTenantId(conf.getTenantId());
        userOutinWarning.setUserId(clockIn.getUserId());
        userOutinWarning.setWarningTime(now);
        userOutinWarning.setStatus(0);
        userOutinWarning.setCreateTime(DateUtil.date());
        userOutinWarning.setModifyTime(userOutinWarning.getCreateTime());

        //类型：1出场异常，2进场异常，3停留异常,默认1
        userOutinWarning.setType(1);
        userOutinWarning.setGroupId(clockIn.getGroupId());

        userOutinWarning.setInClockId(clockIn.getId());
        String rule = LobarConstant.WarningRule.USER_NO_OUT_WARNING.getRule();
        //有进无出，就要判断停留时长

        //判断停留开关
        Integer staySwitch = conf.getUserStaySwitch();
        Integer stayDuration = conf.getUserStayDuration();
        if (staySwitch != null && staySwitch.intValue() == 1 && stayDuration != null) {
            String ymd = DateUtil.format(now, DatePattern.NORM_DATE_PATTERN);
            String outCheck = conf.getUserOutCheck();
            String checkDateStr = ymd + " " + outCheck + ":00";
            Date checkDate = DateUtil.parse(checkDateStr, DatePattern.NORM_DATETIME_PATTERN);
            //判断停留时间
            Long bt = DateUtil.between(clockIn.getClockTime(), checkDate, DateUnit.HOUR);
            if (bt.intValue() >= stayDuration.intValue()) {
                //报
                //rule = LobarConstant.WarningRule.USER_STAY_OUT_WARNING.getRule();
            } else {
                //不报
                return null;
            }
        }

        //预警规则
        EmpUserBaseDTO empUserBaseDTO = userProjectService.getByUserIdAndTenantIEmp(userOutinWarning.getUserId(), userOutinWarning.getTenantId());
        if (null != empUserBaseDTO) {
            String ruleStr = MessageFormat.format(rule,
                    String.valueOf(empUserBaseDTO.getName()),
                    String.valueOf(empUserBaseDTO.getUserProject().getGroupName()),
                    String.valueOf(empUserBaseDTO.getUserProject().getCompanyName()),
                    String.valueOf(conf.getUserOutCheck()), String.valueOf(empUserBaseDTO.getUserProject().getWorkType()), "null", "null", "null", "null", "null");
            userOutinWarning.setWarningRule(ruleStr);
            if (rule.equals(LobarConstant.WarningRule.USER_NO_OUT_WARNING.getRule())) {
                String msgContent = MessageFormat.format(WarningMsgTemplate.USER_NO_OUT_WARNING.getRule(),
                        DateUtil.format(new Date(), com.easylinkin.linkappapi.common.utils.DateUtil.DATE_TIME_FORMAT_MINUTE),
                        String.valueOf(empUserBaseDTO.getUserProject().getGroupName()),
                        String.valueOf(empUserBaseDTO.getName()),
                        String.valueOf(conf.getUserOutCheck()));
                userOutinWarning.setWarningMsg(msgContent);
            }

        }
        return userOutinWarning;

    }

    @Override
    public UserOutinWarning getOneById(Serializable id) {
        return baseMapper.selectById(id);
    }

    /**
     * 校验重复
     */
    private void validRepeat(UserOutinWarning appUserOutinWarning) {
        /* QueryWrapper<UserOutinWarning> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("name", appUserOutinWarning.getName());
        queryWrapper.eq("tenant_id", linkappUserContextProducer.getNotNullCurrent().getTenantId());
        List<UserOutinWarning> list = baseMapper.selectList(queryWrapper);
        if (list.size() == 0) {
            return;
        }
        if (list.size() > 1) {
            throw new BusinessException("名称有重复");
        }
        if (ObjectUtils.isEmpty(appUserOutinWarning.getId())) {
            throw new BusinessException("名称已存在");
        }
        if (!appUserOutinWarning.getId().equals(list.get(0).getId())) {
            throw new BusinessException("名称已存在");
        }
                    */

    }


    /**
     * 校验参数必填
     */
    private void validParamRequired(UserOutinWarning appUserOutinWarning) {
        //Assert.notNull(appUserOutinWarning, "参数为空");
        //Assert.isTrue(StringUtils.isNotBlank(appUserOutinWarning.getName()), "名称为空");
    }

    /**
     * 校验参数格式
     */
    private void validParamFormat(UserOutinWarning appUserOutinWarning) {
        //Assert.isTrue(appUserOutinWarning.getName() == null || appUserOutinWarning.getName().length() <= 50,
        //        "名称超长");
    }
}

